package com.moli.iov.down.remote.impl;

import com.moli.iov.base.model.dto.res.VehicleBaseResDto;
import com.moli.iov.base.model.po.TboxUpgradeFile;
import com.moli.iov.base.remote.ITboxUpgradeFileRemoteService;
import com.moli.iov.base.remote.IVehicleBaseRemoteService;
import com.moli.iov.down.command.SendCommandService;
import com.moli.iov.down.command.design.factory.CommandReqDtoFactory;
import com.moli.iov.down.command.mq.SendCache;
import com.moli.iov.down.enums.FailMsgEnum;
import com.moli.iov.down.enums.OperateStatusEnum;
import com.moli.iov.down.enums.OperateTypeEnum;
import com.moli.iov.down.enums.OperatorResultEnum;
import com.moli.iov.down.model.dto.remoting.CommandDto;
import com.moli.iov.down.model.dto.remoting.CommandReqDto;
import com.moli.iov.down.model.dto.req.VehicleCommandBaseDto;
import com.moli.iov.down.model.dto.req.VehicleCommandDto;
import com.moli.iov.down.model.po.VehicleCommand;
import com.moli.iov.down.model.po.VehicleParam;
import com.moli.iov.down.model.po.extend.FrequencyExtend;
import com.moli.iov.down.model.po.extend.IpAndPortExtend;
import com.moli.iov.down.model.po.extend.VersionExtend;
import com.moli.iov.down.remote.IVehicleCommandRemoteService;
import com.moli.iov.down.remote.IVehicleParamRemoteService;
import com.moli.iov.down.service.IVehicleCommandService;
import com.moli.iov.down.service.IVehicleParamService;
import com.moli.iov.enums.UserTypeEnum;
import com.moli.iov.exception.BusinessException;
import com.moli.iov.rest.RestResponse;
import com.moli.iov.util.ByteArrayUtil;
import com.moli.iov.util.CommonUtils;
import com.moli.iov.util.SnowflakeUtils;
import com.moli.iov.util.StringUtil;
import com.moli.iov.util.ValidationUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * <p>
 * 车辆命令业务
 * </p>
 *
 * @author zql
 * @since 2019/9/17 9:58
 */
@Slf4j
@Service(group = "${service.group}")
public class VehicleCommandRemoteServiceImpl implements IVehicleCommandRemoteService {

  @Autowired
  private IVehicleCommandService vehicleCommandService;

  @Autowired
  private SendCache sendCache;

  @Autowired
  private SendCommandService sendCommandService;

  @Reference(group = "${service.group}", check = false)
  private IVehicleBaseRemoteService vehicleBaseRemoteService;

  @Reference(group = "${service.group}", check = false)
  private ITboxUpgradeFileRemoteService tboxUpgradeFileRemoteService;

  @Autowired
  private IVehicleParamRemoteService vehicleParamRemoteService;

  @Autowired
  private IVehicleParamService vehicleParamService;

  /**
   * 控车
   * @param vehicleCommandDto
   * @return
   */
  @Override
  public RestResponse sendCommand(VehicleCommandDto vehicleCommandDto) {

    // 参数校验， 返回车辆信息
    sendControlValid(vehicleCommandDto);

    // 组装数据
    List<VehicleCommand> vehicleCommands = assemblyVehicleCommand(vehicleCommandDto);

    // 保存数据
    vehicleCommandService.batchSaveCommand(vehicleCommands);

    //组装命令请求DTO
    List<CommandDto> commandDtos = assemblyCommandReqDto(vehicleCommands);

    if(commandDtos.isEmpty()){
      return RestResponse.failure("车辆离线");
    }

    // 发送命令
    sendCommandService.sendCommand(commandDtos);
    return RestResponse.success();
  }

  /**
   * 参数校验
   * @param vehicleCommandDto
   * @return
   */
  private VehicleCommandDto sendControlValid(VehicleCommandDto vehicleCommandDto){

    ValidationUtils.restResponseValidate(vehicleCommandDto, VehicleCommandDto.Control.class, VehicleCommandDto.Base.class);

    List<String> onlineVins =
        vehicleParamRemoteService.getOnlineVins(Arrays.asList(vehicleCommandDto.getVin()));
    if(!onlineVins.contains(vehicleCommandDto.getVin())){
      throw new BusinessException(FailMsgEnum.OFFLINE.getMsg());
    }

    //查询车辆信息
    RestResponse<VehicleBaseResDto> vehicleCompanyIdByVin = vehicleBaseRemoteService
        .getVehicleCompanyIdByVin(vehicleCommandDto.getVin());
    if(!vehicleCompanyIdByVin.flag){
      throw new BusinessException(vehicleCompanyIdByVin.getMessage());
    }

    VehicleBaseResDto vehicleBaseResDto = vehicleCompanyIdByVin.getData();

    boolean isAuth = UserTypeEnum.COMPANY.getFlag() == vehicleCommandDto.getSysUserType() &&
        vehicleCommandDto.getSysCompanyId() != null &&
        !vehicleCommandDto.getSysCompanyId().equals(vehicleBaseResDto.getCompanyId());
    if(isAuth){
      throw new BusinessException("无当前车辆权限");
    }

    vehicleCommandDto.setCompanyId(vehicleBaseResDto.getCompanyId());
    vehicleCommandDto.setCompanyName(vehicleBaseResDto.getCompanyName());
    vehicleCommandDto.setLicense(vehicleBaseResDto.getLicense());
    return vehicleCommandDto;
  }

  /**
   * 组装 VehicleCommand
   * @param vehicleCommandDto
   * @return
   */
  private List<VehicleCommand> assemblyVehicleCommand(VehicleCommandDto vehicleCommandDto){
    VehicleParam vehicleParamByVin =
        vehicleParamService.getVehicleParamByVin(vehicleCommandDto.getVin());

    List<VehicleCommand> vehicleCommands = new ArrayList<>();

    vehicleCommandDto.getOperateTypes().forEach(operateType -> {

      VehicleCommand vehicleCommand = new VehicleCommand();
      CommonUtils.copyProperties(vehicleCommandDto, vehicleCommand);

      vehicleCommand.setId(SnowflakeUtils.getId());

      vehicleCommand.setOperateType(operateType);
      vehicleCommand.setOperateTypeName(OperateTypeEnum.getMsg(operateType));
      vehicleCommand.setCommandNo(sendCache.getSerialNumber(vehicleCommandDto.getVin()));
      vehicleCommand.setStatus(OperateStatusEnum.STAY_SEND.getFlag());
      vehicleCommand.setResult(OperatorResultEnum.INIT.getFlag());
      vehicleCommand.setCreateTime(new Date());
      if(vehicleParamByVin != null){
        vehicleCommand.setIccid(vehicleParamByVin.getIccid());
      }

      if(vehicleParamByVin != null &&
          OperateTypeEnum.FREQUENCY.getFlag() == vehicleCommand.getOperateType()){
        vehicleCommand.setFrequencyExtend(
            new FrequencyExtend(vehicleParamByVin.getReportingFrequency(), vehicleCommandDto.getFrequency()));
      }

      if(vehicleParamByVin != null &&
          OperateTypeEnum.CHANGE_IP_PORT.getFlag() == vehicleCommand.getOperateType()){
        vehicleCommand.setIpAndPortExtend(
            new IpAndPortExtend(vehicleParamByVin.getPlatformDomainName(), vehicleCommandDto.getIp(),
                vehicleParamByVin.getPlatformPort(), vehicleCommandDto.getPort()));
      }

      if(vehicleParamByVin != null &&
          OperateTypeEnum.UPGRADE.getFlag() == vehicleCommand.getOperateType()){
        assemblyUpgradeInfo(vehicleCommand, vehicleParamByVin.getDeviceSoftVersion());
      }

      vehicleCommands.add(vehicleCommand);
    });
    return vehicleCommands;
  }

  /**
   * 组装命令 组装升级命令信息
   * @param vehicleCommand
   * @param deviceSoftVersion
   * @return
   */
  public VehicleCommand assemblyUpgradeInfo(VehicleCommand vehicleCommand, String deviceSoftVersion) {

    RestResponse<Map<String, TboxUpgradeFile>> restResponse =
        tboxUpgradeFileRemoteService.getUpgradeFileByVin(Arrays.asList(vehicleCommand.getVin()));
    if(restResponse == null || restResponse.getData() == null){
      log.info("未查到升级url、版本信息 vin:{}", vehicleCommand.getVin());
      throw new BusinessException("未查到升级url");
    }

    Map<String, TboxUpgradeFile> tboxUpgradeFileMap = restResponse.getData();
    TboxUpgradeFile tboxUpgradeFile = tboxUpgradeFileMap.get(vehicleCommand.getVin());
    if(tboxUpgradeFile == null){
      log.info("未查到升级url、版本信息 vin:{}", vehicleCommand.getVin());
      throw new BusinessException("未查到升级url");
    }

    vehicleCommand.setVersionExtend(
        new VersionExtend(deviceSoftVersion, tboxUpgradeFile.getSoftwareVersion()));
    vehicleCommand.setUpgradeUrl(tboxUpgradeFile.getFileUrl());

    return vehicleCommand;
  }

  /**
   * 组装命令DTO
   * @param vehicleCommands
   * @return
   */
  private List<CommandDto> assemblyCommandReqDto(List<VehicleCommand> vehicleCommands){
    List<CommandDto> commandDtos = new ArrayList<>();
    vehicleCommands.forEach(vehicleCommand -> {

      if(vehicleCommand.getStatus() != OperateStatusEnum.STAY_SEND.getFlag()){
        return;
      }

      Integer frequency =
          vehicleCommand.getFrequencyExtend() != null ? vehicleCommand.getFrequencyExtend().getFrequency() : null;
      String ip = vehicleCommand.getIpAndPortExtend() != null ? vehicleCommand.getIpAndPortExtend().getIp() : null;
      Integer port = vehicleCommand.getIpAndPortExtend() != null ? vehicleCommand.getIpAndPortExtend().getPort() : null;

      CommandReqDto commandReqDto = new CommandReqDto();
      CommonUtils.copyProperties(vehicleCommand, commandReqDto);
      commandReqDto.setIsRetry(OperateTypeEnum.isRetry(vehicleCommand.getOperateType()));
      commandReqDto.setFrequency(frequency);
      commandReqDto.setIp(ip);
      commandReqDto.setPort(port);

      CommandDto commandDto = CommandReqDtoFactory.getCommandReqDto(commandReqDto);
      commandDto.setCommandNo(vehicleCommand.getCommandNo());
      commandDto.setUrl(vehicleCommand.getUpgradeUrl());
      commandDtos.add(commandDto);
    });

    return commandDtos;
  }

  /**
   * 应答发送
   *
   * @param vin          唯一标识
   * @param command      命令
   * @param responseTag  应答标志
   * @param serialNumber 流水号
   * @return
   */
  @Override
  public RestResponse sendResponse(String vin, Byte command, Byte responseTag,
      byte[] serialNumber) {
    if (StringUtil.isBlank(vin)) {
      log.error("[应答发送] vin 不能为");
      return RestResponse.failure("vin 不能为空");
    }

    if (command == null) {
      log.error("[应答发送] 命令 不能为");
      return RestResponse.failure("命令 不能为空");
    }

    if (responseTag == null) {
      log.error("[应答发送] 应答标志不能为空");
      return RestResponse.failure("应答标志不能为空");
    }

    Long vSerialNumber = Long.parseLong(String.valueOf(ByteArrayUtil.toInt(serialNumber)));
    CommandDto commandDto = new CommandDto();
    commandDto.setVin(vin);
    commandDto.setCommand(command);
    commandDto.setCommandNo(vSerialNumber);
    commandDto.setResponseTag(responseTag);
    return sendCommandService.sendResponse(commandDto);
  }

  /**
   * 升级命令
   * @param vehicleCommandDto
   * @return
   */
  @Override
  public RestResponse sendUpgrade(VehicleCommandDto vehicleCommandDto) {

    // 参数校验， 返回车辆信息
    sendUpgradeValid(vehicleCommandDto);

    // 组装数据
    List<VehicleCommand> vehicleCommands = assemblyVehicleCommandOfUpgrade(vehicleCommandDto);

    // 保存数据
    vehicleCommandService.batchSaveCommand(vehicleCommands);

    //组装命令请求DTO
    List<CommandDto> commandDtos = assemblyCommandReqDto(vehicleCommands);

    if(commandDtos.isEmpty()){
      return RestResponse.failure("车辆离线");
    }

    // 发送命令
    sendCommandService.sendCommand(commandDtos);
    return RestResponse.success();
  }

  /**
   * 参数校验
   * @param vehicleCommandDto
   * @return
   */
  private VehicleCommandDto sendUpgradeValid(VehicleCommandDto vehicleCommandDto){

    ValidationUtils.restResponseValidate(vehicleCommandDto, VehicleCommandDto.Control.class, VehicleCommandDto.Base.class);
    List<String> vins = vehicleCommandDto.getVins();
    if(vins == null || vins.isEmpty()){
      vins = Arrays.asList(vehicleCommandDto.getVin());
    }

    List<String> onlineVins =
        vehicleParamRemoteService.getOnlineVins(vins);
    if(onlineVins == null || onlineVins.isEmpty()){
      throw new BusinessException(FailMsgEnum.OFFLINE.getMsg());
    }

    Map<String, VehicleCommandBaseDto> vehicleCommandBaseDtos = new HashMap<>();
    for(String vin : vins){
      //查询车辆信息
      RestResponse<VehicleBaseResDto> vehicleCompanyIdByVin = vehicleBaseRemoteService
          .getVehicleCompanyIdByVin(vin);
      if(!vehicleCompanyIdByVin.flag){
        throw new BusinessException(vehicleCompanyIdByVin.getMessage());
      }

      VehicleBaseResDto vehicleBaseResDto = vehicleCompanyIdByVin.getData();

      boolean isAuth = UserTypeEnum.COMPANY.getFlag() == vehicleCommandDto.getSysUserType() &&
          vehicleCommandDto.getSysCompanyId() != null &&
          !vehicleCommandDto.getSysCompanyId().equals(vehicleBaseResDto.getCompanyId());
      if(isAuth){
        throw new BusinessException("无当前车辆权限");
      }


      VehicleCommandBaseDto  vehicleCommandBaseDto = new VehicleCommandBaseDto();
      vehicleCommandBaseDto.setCompanyId(vehicleBaseResDto.getCompanyId());
      vehicleCommandBaseDto.setCompanyName(vehicleBaseResDto.getCompanyName());
      vehicleCommandBaseDto.setLicense(vehicleBaseResDto.getLicense());
      vehicleCommandBaseDto.setOnline(onlineVins.contains(vin));
      vehicleCommandBaseDtos.put(vin, vehicleCommandBaseDto);
    }

    vehicleCommandDto.setVehicleCommandBaseDtoMap(vehicleCommandBaseDtos);

    return vehicleCommandDto;
  }

  /**
   * 批量组装 VehicleCommand
   * @param vehicleCommandDto
   * @return
   */
  public List<VehicleCommand> assemblyVehicleCommandOfUpgrade(VehicleCommandDto vehicleCommandDto){

    List<VehicleCommand> vehicleCommands = new ArrayList<>();
    Map<String, VehicleCommandBaseDto> vehicleCommandBaseDtoMap =
        vehicleCommandDto.getVehicleCommandBaseDtoMap();

    if(StringUtil.isNotBlank(vehicleCommandDto.getVin())){

      return assemblyVehicleCommand(vehicleCommands, vehicleCommandDto, vehicleCommandBaseDtoMap);
    }

    if(vehicleCommandDto.getVins() != null &&
        !vehicleCommandDto.getVins().isEmpty()){

      vehicleCommandDto.getVins().forEach(vin->{

        vehicleCommandDto.setVin(vin);

        assemblyVehicleCommand(vehicleCommands, vehicleCommandDto, vehicleCommandBaseDtoMap);
      });
    }
    return vehicleCommands;
  }

  private List<VehicleCommand> assemblyVehicleCommand(List<VehicleCommand> vehicleCommands,
      VehicleCommandDto vehicleCommandDto, Map<String, VehicleCommandBaseDto> vehicleCommandBaseDtoMap){

    VehicleCommand vehicleCommand = assemblyVehicleCommandSingle(vehicleCommandDto);

    VehicleCommandBaseDto vehicleCommandBaseDto = vehicleCommandBaseDtoMap
        .get(vehicleCommandDto.getVin());

    CommonUtils.copyProperties(vehicleCommandBaseDto, vehicleCommand);
    if(!vehicleCommandBaseDto.isOnline()){
      vehicleCommand.setStatus(OperateStatusEnum.FAIL.getFlag());
      vehicleCommand.setResult(OperatorResultEnum.FAIL.getFlag());
      vehicleCommand.setResultMemo(FailMsgEnum.OFFLINE.getMsg());
    }

    vehicleCommands.add(vehicleCommand);
    return vehicleCommands;
  }

  /**
   * 组装 单个VehicleCommand
   * @param vehicleCommandDto
   * @return
   */
  private VehicleCommand assemblyVehicleCommandSingle(VehicleCommandDto vehicleCommandDto){
    VehicleParam vehicleParamByVin =
        vehicleParamService.getVehicleParamByVin(vehicleCommandDto.getVin());

    List<VehicleCommand> vehicleCommands = new ArrayList<>();

    int operateType = vehicleCommandDto.getOperateTypes().get(0);

    VehicleCommand vehicleCommand = new VehicleCommand();
    CommonUtils.copyProperties(vehicleCommandDto, vehicleCommand);


    vehicleCommand.setId(SnowflakeUtils.getId());

    vehicleCommand.setOperateType(operateType);
    vehicleCommand.setOperateTypeName(OperateTypeEnum.getMsg(operateType));
    vehicleCommand.setCommandNo(sendCache.getSerialNumber(vehicleCommandDto.getVin()));
    vehicleCommand.setStatus(OperateStatusEnum.STAY_SEND.getFlag());
    vehicleCommand.setResult(OperatorResultEnum.INIT.getFlag());
    vehicleCommand.setCreateTime(new Date());
    if(vehicleParamByVin != null){
      vehicleCommand.setIccid(vehicleParamByVin.getIccid());
    }

    if(vehicleParamByVin != null &&
        OperateTypeEnum.FREQUENCY.getFlag() == vehicleCommand.getOperateType()){
      vehicleCommand.setFrequencyExtend(
          new FrequencyExtend(vehicleParamByVin.getReportingFrequency(), vehicleCommandDto.getFrequency()));
    }

    if(vehicleParamByVin != null &&
        OperateTypeEnum.CHANGE_IP_PORT.getFlag() == vehicleCommand.getOperateType()){
      vehicleCommand.setIpAndPortExtend(
          new IpAndPortExtend(vehicleParamByVin.getPlatformDomainName(), vehicleCommandDto.getIp(),
              vehicleParamByVin.getPlatformPort(), vehicleCommandDto.getPort()));
    }

    if(vehicleParamByVin != null &&
        OperateTypeEnum.UPGRADE.getFlag() == vehicleCommand.getOperateType()){
      vehicleCommand.setVersionExtend(
          new VersionExtend(vehicleParamByVin.getDeviceSoftVersion(), vehicleCommandDto.getVersion()));
      vehicleCommand.setUpgradeUrl(vehicleCommandDto.getUpgradeUrl());

    }

    vehicleCommands.add(vehicleCommand);
    return vehicleCommand;
  }

}
